package main

import (
	"context"
	"errors"
	"fmt"
	"github.com/360EntSecGroup-Skylar/excelize"
	"go.manyji.com/lib/app"
	"go.manyji.com/lib/json"
	"go.manyji.com/lib/log"
	"go.manyji.com/lib/mysql"
	"go.manyji.com/lib/util/convert"
	"net/url"
	"reflect"
	"sieve_agent_server/lib/server"
	"strconv"
	"strings"
	"time"
)

func init() {
	app.Async(func() {
		WaitRoleInit()

		if err := (&mod_BusinessPrice{}).SyncBusinessPriceUserCount(); err != nil {
			panic(err)
		}
	})
}

type mod_BusinessPrice struct {
}

/*	========================================================================= 价格配置相关接口 ========================================================================= */

func (a *mod_BusinessPrice) API_add_item(ctx *server.Context) interface{} {

	var (
		uid  = uint64(ctx.Uid)
		now  = time.Now().Unix()
		item = map[string]interface{}{
			"creator":    uid,
			"create_at":  now,
			"updated_at": now,
			"role_id":    (&data_Role{}).GetAgentRole().ID,
		}
		c = context.Background()
	)

	if ctx.Args.Has("name") && ctx.Args.Get("name") != "" {
		name := ctx.Args.Get("name")
		item["name"] = name
		if data, err := a.GetByName(c, name); err == nil && data != nil && data.Name == name {
			return errors.New("已存在相同的方案名称")
		}
	} else {
		return errors.New(fmt.Sprintf("'方案名称'为必填项"))
	}

	if ctx.Args.Has("prices") && ctx.Args.Get("prices") != "" {
		item["prices"] = ctx.Args.Get("prices")
		if err := a.checkBusinessPrice(c, json.RawMessage(item["prices"].(string))); err != nil {
			return err
		}
	} else {
		return errors.New(fmt.Sprintf("'方案价格'为必填项"))
	}

	if ctx.Args.Has("description") && ctx.Args.Get("description") != "" {
		item["description"] = ctx.Args.Get("description")
	}

	if ctx.Args.Has("def") && ctx.Args.Get("def") != "" {
		defValue := uint64(convert.Atoi(ctx.Args.Get("def")))
		item["def"] = defValue
	}

	_, err := db.Create(context.Background(), "business_price_scheme", item)
	return err
}

/*
	 prices 的格式:
		[{"name":"filter","unit_price":0.006},{"name":"gender","unit_price":0.006}]
*/
func (a *mod_BusinessPrice) API_edit_item(ctx *server.Context) interface{} {

	var (
		now        = time.Now().Unix()
		queryKeys  = map[string]interface{}{}
		updateKeys = map[string]interface{}{
			"updated_at": now,
			"creator":    uint64(ctx.Uid),
			"role_id":    (&data_Role{}).GetAgentRole().ID,
		}
		c = context.Background()
	)

	if ctx.Args.Has("id") && ctx.Args.Get("id") != "" {
		if id, err := strconv.ParseUint(ctx.Args.Get("id"), 10, 64); err != nil {
			return errors.New("请求参数错误")
		} else {
			queryKeys["id"] = id
		}
	} else {
		return errors.New("请求参数缺失")
	}

	if ctx.Args.Has("name") && ctx.Args.Get("name") != "" {
		name := ctx.Args.Get("name")
		if data, err := a.GetByName(c, name); err == nil && data != nil && data.Name != name {
			return errors.New("不存在的方案名称")
		}
		updateKeys["name"] = name
	}

	if ctx.Args.Has("prices") && ctx.Args.Get("prices") != "" {

		prices := ctx.Args.Get("prices")
		updateKeys["prices"] = prices
		if err := a.checkBusinessPrice(c, json.RawMessage(prices)); err != nil {
			return err
		}
	}

	if ctx.Args.Has("description") && ctx.Args.Get("description") != "" {

		updateKeys["description"] = ctx.Args.Get("description")
	}

	if err := db.Update(context.Background(), "business_price_scheme", updateKeys, queryKeys); err != nil {
		log.Err(fmt.Sprintf("update user status err:%+v", err))
		return err
	}

	return nil
}

/*
删除的同时需要指定一个其他的业务方案
*/
func (a *mod_BusinessPrice) API_del_item(ctx *server.Context) interface{} {

	var (
		delId uint64
		newId uint64
		c     = context.Background()
	)
	if ctx.Args.Has("id") && ctx.Args.Get("id") != "" {
		if id, err := strconv.ParseUint(ctx.Args.Get("id"), 10, 64); err != nil {
			return errors.New("请求参数错误")
		} else {
			delId = id
		}
	} else {
		return errors.New("请指定要删除的项")
	}

	if ctx.Args.Has("new_id") && ctx.Args.Get("new_id") != "" {
		if id, err := strconv.ParseUint(ctx.Args.Get("new_id"), 10, 64); err != nil {
			return errors.New("请求参数错误")
		} else {
			newId = id
		}
	} else {
		return errors.New("请指定要新的项")
	}

	return db.TransactionTx(c, func(ctx context.Context, t *mysql.Tx) error {

		var (
			updateKeys = map[string]interface{}{
				"business_price_id": newId,
			}
			queryKeys = map[string]interface{}{
				"business_price_id": delId,
			}
		)

		// 1. 更新 new_id 的用户数量
		if err := (&data_User{}).UpdateBusinessPriceUserCount(ctx, newId, false); err != nil {
			return err
		}

		// 2. 更新 用户的价格方案
		if err := db.Update(ctx, "user", updateKeys, queryKeys); err != nil {
			return err
		}

		// 3. 将旧的价格方案删除
		return db.SoftDelete(context.Background(), "business_price_scheme", delId)
	})
}

func (a *mod_BusinessPrice) API_query_list(ctx *server.Context) interface{} {

	var (
		pageSize       = 99999
		pageIndex      = 1
		c              = context.Background()
		queryCondition []string
	)

	if ctx.Args.Has("page_size") && ctx.Args.Get("page_size") != "" {
		pageSize = convert.Atoi(ctx.Args.Get("page_size"))
	}

	if ctx.Args.Has("page_index") && ctx.Args.Get("page_index") != "" {
		pageIndex = convert.Atoi(ctx.Args.Get("page_index"))
	}

	if ctx.Args.Has("id") && ctx.Args.Get("id") != "" {
		queryCondition = append(queryCondition, fmt.Sprintf(" id = %v", ctx.Args.Get("id")))
	}

	if ctx.Args.Has("name") && ctx.Args.Get("name") != "" {
		queryCondition = append(queryCondition, fmt.Sprintf(" name = %v", ctx.Args.Get("name")))
	}

	if ctx.Args.Has("description") && ctx.Args.Get("description") != "" {
		queryCondition = append(queryCondition, fmt.Sprintf(" description = %v", ctx.Args.Get("description")))
	}

	var (
		withDef bool
		defData = &BusinessPriceQueryRsp{}
	)
	if ctx.Args.Has("with_def") && ctx.Args.Get("with_def") != "" {

		params := &server.Context{
			Args: url.Values{
				"page_index": {"1"},
				"page_size":  {"1"},
				"def":        {"1"},
			},
		}
		defData = a.queryDetail(params)
		defData.UserCount = int64(a.queryDefCount(defData.ID, ctx))
		withDef = true
		ctx.Args.Set("def", "2")
	}

	defCondition := []string{
		"business_price_scheme.deleted = 0",
	}

	// 只可以查询管理端的默认的价格表
	if ctx.Args.Has("def") && ctx.Args.Get("def") != "" && ctx.Args.Get("def") == "1" {
		defCondition = append(defCondition, fmt.Sprintf(" (business_price_scheme.def = %v AND business_price_scheme.role_id = %v)", ctx.Args.Get("def"), (&data_Role{}).GetAdminRole().ID))
	} else {
		defCondition = append(defCondition, []string{
			fmt.Sprintf("business_price_scheme.role_id = %v", (&data_Role{}).GetAgentRole().ID),
			fmt.Sprintf("business_price_scheme.creator = %v", ctx.Uid),
		}...)
	}

	queryCondition = append(queryCondition, defCondition...)

	var (
		condition     = fmt.Sprintf(`WHERE %v `, strings.Join(queryCondition, " AND "))
		queryCountSQL = fmt.Sprintf(`
	SELECT 
	    COUNT(*)  as count
	FROM business_price_scheme
		%v
`, condition)
		querySQL = fmt.Sprintf(`
SELECT
  business_price_scheme.id AS id,
  business_price_scheme.name AS name,
  business_price_scheme.description AS description,
  business_price_scheme.user_count AS user_count,
  business_price_scheme.prices AS prices,
  business_price_scheme.def AS def,
  business_price_scheme.status AS status,
  business_price_scheme.role_id AS role_id,
  business_price_scheme.creator AS creator,
  business_price_scheme.create_at AS create_at,
  business_price_scheme.updated_at AS updated_at
FROM business_price_scheme
%v 	ORDER BY create_at DESC %v 
`, condition, fmt.Sprintf(" LIMIT %v OFFSET %v", pageSize, (pageIndex-1)*pageSize))
	)

	log.Info("query count ", queryCountSQL)
	respCount, err := db.QueryRaw(c, queryCountSQL, QueryCount{})
	if err != nil {
		return err
	}

	if respCount == nil {
		return nil
	}

	count := respCount[0].(*QueryCount).Count

	respBpList, err := db.QueryRaw(c, querySQL, BusinessPriceQueryRsp{})
	if err != nil {
		return err
	}

	if withDef && defData != nil {
		//if len(respBpList) > 0 {
		//	respBpList = append([]interface{}{defData}, respBpList...)
		//}

		respBpList = append([]interface{}{defData}, respBpList...)
	}
	ret := map[string]interface{}{
		"list":  respBpList,
		"total": count,
	}
	return ret
}

func (a *mod_BusinessPrice) API_queryDetail_item(ctx *server.Context) interface{} {

	return a.QueryDetail(ctx)
}

func (a *mod_BusinessPrice) API_export_list(ctx *server.Context) interface{} {

	queryCondition := []string{
		fmt.Sprintf("business_price_scheme.role_id = %v", (&data_Role{}).GetAgentRole().ID),
		fmt.Sprintf("business_price_scheme.creator = %v", ctx.Uid),
	}
	if ctx.Args.Has("ids") && ctx.Args.Get("ids") != "" {
		queryCondition = append(queryCondition, fmt.Sprintf("business_price_scheme.id IN (%v)", strings.Join(strings.Split(ctx.Args.Get("ids"), "|"), ",")))
	} else {
		return errors.New("请求参数缺失")
	}

	var (
		condition = fmt.Sprintf(`WHERE %v `, strings.Join(queryCondition, " AND "))
		querySQL  = fmt.Sprintf(`
SELECT
  business_price_scheme.id AS id,
  business_price_scheme.name AS name,
  business_price_scheme.description AS description,
  business_price_scheme.user_count AS user_count,
  business_price_scheme.prices AS prices,
  business_price_scheme.def AS def,
  business_price_scheme.status AS status,
  business_price_scheme.role_id AS role_id,
  business_price_scheme.creator AS creator,
  business_price_scheme.create_at AS create_at,
  business_price_scheme.updated_at AS updated_at
FROM business_price_scheme
%v 	ORDER BY create_at DESC
`, condition)
	)
	respTaskList, err := db.QueryRaw(context.Background(), querySQL, BusinessPriceQueryRsp{})
	if err != nil {
		return err
	}

	selectedItems := []string{
		"name",
		"description",
		"user_count",
		"updated_at",
	}

	req := a.export(context.Background(), respTaskList, 1, strings.Join(selectedItems, "|"))
	if req != nil {
		if _, ok := req.(error); ok {
			return req
		}
	}

	return req
}

func (a *mod_BusinessPrice) export(ctx context.Context, req interface{}, funcTy int, selectedItems string) interface{} {

	var (
		xlsx       = excelize.NewFile()
		categories = map[string]string{
			"A1": "业务方案",
			"B1": "说明",
			"C1": "用户数",
			"D1": "最后修改时间",
		}
		selectedItemMap = make(map[string]int)
		colWidths       = map[string]float64{"A": 10, "B": 30, "C": 8, "D": 20}
	)

	for key, value := range categories {
		xlsx.SetCellValue("Sheet1", key, value)
		_style, err := xlsx.NewStyle(`{"fill":{"type":"pattern","color":["#EEECE1"],"pattern":1},"alignment":{"horizontal":"center","vertical":"center"},"font":{"bold":true,"family":"宋体","size":10}}`)
		if err == nil {
			xlsx.SetCellStyle("Sheet1", key, key, _style)
		}
	}

	xlsx.SetRowHeight("Sheet1", 1, 20)

	for k, v := range colWidths {
		xlsx.SetColWidth("Sheet1", k, k, v)
	}

	for i, v := range strings.Split(fmt.Sprintf("%v", selectedItems), "|") {
		selectedItemMap[v] = i
	}

	for index, vv := range req.([]interface{}) {

		var (
			columIndex  = 'A'
			columOffset = 1
		)

		for kkk, vvv := range Struct2Map(vv, "json") {

			if len(selectedItemMap) > 0 {
				if i, ok := selectedItemMap[kkk]; !ok {
					continue
				} else {
					columOffset = i
				}
			}

			cell := fmt.Sprintf("%c", int(columIndex)+columOffset) + convert.ToString(index+2)

			if reflect.TypeOf(vvv).Kind() == reflect.Ptr {
				vOf := reflect.ValueOf(vvv)
				if vOf.IsNil() {
					vvv = ""
				} else {
					vvv = vOf.Elem()
				}
			}

			if kkk == "updated_at" {
				vvv = Format(vvv.(int64))
			}

			xlsx.SetCellValue("Sheet1", cell, vvv)
		}

		xlsx.SetRowHeight("Sheet1", index+2, 18)
	}

	xlsx.SetSheetName("Sheet1", "第一页")

	fileName, filePath := a.getExportPath(convert.ToString(time.Now().UnixMilli())+".csv", funcTy)
	err := xlsx.SaveAs(fileName)
	if err != nil {
		return nil
	}

	req = filePath

	return req
}

func (a *mod_BusinessPrice) getExportPath(fileName string, bizTy int) (string, string) {

	var (
		bizTyMap = map[int]string{
			1: "business_price",
		}
	)

	relativePath := fmt.Sprintf("%v/export/%v", bizTyMap[bizTy], fileName)

	return GetFilePath(
		fmt.Sprintf("%v/%v",
			FileStorage,
			relativePath),
	), relativePath
}

/*	========================================================================= 价格配置相关接口 ========================================================================= */

func (a *mod_BusinessPrice) queryDefCount(businessPriceId uint64, ctx *server.Context) uint64 {
	params := &server.Context{
		Uid: ctx.Uid,
		Args: url.Values{
			"page_index":        {"1"},
			"page_size":         {"1"},
			"only_count":        {"1"},
			"business_price_id": {fmt.Sprintf("%v", businessPriceId)},
		},
	}

	var count uint64
	if resp := (&mod_User{}).API_query_list(params); resp != nil {

		if v, ok := resp.(map[string]interface{})["total"]; ok {
			if v, ok := v.(uint64); ok {
				count = v
			}
		}
	}
	return count
}

func (a *mod_BusinessPrice) queryDetail(ctx *server.Context) *BusinessPriceQueryRsp {

	var defData *BusinessPriceQueryRsp
	params := ctx

	if resp := a.API_query_list(params); resp != nil {

		if v, ok := resp.(map[string]interface{})["list"]; ok {
			if v, ok := v.([]interface{}); ok {
				if len(v) > 0 {
					defData = v[0].(*BusinessPriceQueryRsp)
				}
			}
		}
	}

	return defData
}

// 规则为：不得低于平台价格。不得高于平台价格两倍
func (a *mod_BusinessPrice) checkBusinessPrice(ctx context.Context, prices json.RawMessage) error {

	var (
		name2Price = map[string]uint64{}
		bizTyData  []BusinessTypeQueryRsp
		params     = &server.Context{
			Args: url.Values{
				"page_index": {"1"},
				"page_size":  {"1"},
				"def":        {"1"},
			},
		}
	)
	if err := json.Unmarshal(prices, &bizTyData); err != nil {
		return err
	} else {
		for _, v := range bizTyData {
			name2Price[v.Name] = v.UnitPrice
		}
	}

	resp := a.QueryDetail(params)
	if resp == nil {
		return nil
	}
	if v, ok := resp.(error); ok {
		return v
	}

	if len(name2Price) < len(resp.([]interface{})) {
		return errors.New(fmt.Sprintf("请设置所有的业务类型价格后再提交"))
	}

	for _, v := range resp.([]interface{}) {
		item := v.(*BusinessTypeQueryRsp)
		if setPrice, ok := name2Price[item.Name]; ok {
			if !(setPrice >= item.UnitPrice && setPrice <= item.UnitPrice*2) {
				return errors.New(fmt.Sprintf("设置的'%v(%v)'的价格(%v)不在有效范围内(%v-%v)",
					item.Desc,
					item.Name, float64(setPrice)/10000.0, float64(item.UnitPrice)/10000, float64(item.UnitPrice*2)/10000))
			}
		}
	}

	return nil
}

func (a *mod_BusinessPrice) QueryDetail(ctx *server.Context) interface{} {

	var names []string
	if dataDef := a.queryDetail(ctx); dataDef != nil {
		var vv []BusinessTypeQueryRsp
		if err := json.Unmarshal(dataDef.Prices, &vv); err != nil {
			return err
		} else {
			for _, v := range vv {
				names = append(names, fmt.Sprintf("'%v'", v.Name))
			}
		}
	}

	if len(names) == 0 {
		return nil
	}

	var (
		querySQL = fmt.Sprintf(`
	SELECT
		id,
		name,
		%v,
		unit_price
	FROM
	    business_type_configuration
	WHERE 
	    name IN (%v) AND sieve_source_config_id > 0 AND status = 1 AND deleted = 0 
`, "`desc`", strings.Join(names, ","))
	)

	respBiz, err := db.QueryRaw(context.Background(), querySQL, BusinessTypeQueryRsp{})
	if err != nil {
		return err
	}

	return respBiz
}

func (a *mod_BusinessPrice) GetByName(ctx context.Context, name string) (*BusinessPriceScheme, error) {

	var (
		querySQL = fmt.Sprintf(`
	SELECT * FROM %v 
	WHERE name = '%v'
	AND deleted = 0
`, "business_price_scheme", name)
	)

	req, err := db.QueryRaw(ctx, querySQL, BusinessPriceScheme{})
	if err != nil {
		return nil, err
	}

	if req == nil {
		return nil, errors.New(fmt.Sprintf("record not  found"))
	}

	return req[0].(*BusinessPriceScheme), nil
}

/*	========================================================================= 价格配置相关接口 ========================================================================= */

func (a *mod_BusinessPrice) SyncBusinessPriceUserCount(args ...interface{}) error {

	var (
		execSQL = fmt.Sprintf(`
UPDATE 
	business_price_scheme
INNER JOIN (
SELECT 
	business_price_scheme.id as business_price_id,
	COUNT(user.id) AS user_count
FROM
	business_price_scheme
LEFT JOIN user ON business_price_scheme.id = user.business_price_id
AND user.deleted = 0 AND user.role_id = %v
GROUP BY business_price_scheme.id
) user_business_price ON user_business_price.business_price_id = business_price_scheme.id AND business_price_scheme.deleted = 0
SET business_price_scheme.user_count = user_business_price.user_count;
`, (&data_Role{}).GetChannelAgentRole().ID)
	)

	_, err := db.ExecRaw(context.Background(), execSQL)
	return err
}
